<html>
<head>
  <title>6. 适配器（Adapter）模式</title>
  <basefont face="微软雅黑" size="2" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name="exporter-version" content="YXBJ Windows/602628 (zh-CN, DDL); Windows/10.0.0 (Win64); EDAMVersion=V2;"/>
  <style>
    body, td {
      font-family: 微软雅黑;
      font-size: 10pt;
    }
  </style>
</head>
<body>
<a name="21319"/>
<h1>6. 适配器（Adapter）模式</h1>
<div>
<table bgcolor="#D4DDE5" border="0">
<tr><td><b>创建时间：</b></td><td><i>2020/5/10 10:11</i></td></tr>
<tr><td><b>更新时间：</b></td><td><i>2021/1/9 8:02</i></td></tr>
<tr><td><b>作者：</b></td><td><i>Long Life Student</i></td></tr>
</table>
</div>
<br/>

<div>
<span><div><div><span style="font-size: 14pt; font-weight: bold;">一、基本描述</span></div><ul><li><div>将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。</div></li><li><div>适配器模式主要应用于希望复用一些现存的类，但接口又与复用环境要求不一致的情况。</div></li><li><div>适配器模式不应滥用：只有在双方都不太容易修改的时候再使用适配器，否则应优先考虑通过重构统一接口。</div></li><li><div>事后适配不如事中重构，事中重构不如事前预防。</div></li><li><div>适配器模式是对于意外的补救模式，解决接口不相容问题。</div></li><li><div>适配器模式又称为变压器模式，包装模式（Wrapper）。</div></li><li><div>适配器并不完美，客户必须小心潜在的异常，但只要客户够小心，而且适配器的文档能做出说明，这也算是一个合理的解决方案。</div></li><li><div>客户使用适配器的过程如下：</div></li><ul><li><div>客户通过目标接口调用适配器的方法对适配器发出请求</div></li><li><div>适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口</div></li><li><div>客户接收到调用的结果，但并未察觉这一切是适配器在起转换作用</div></li></ul></ul><div style="margin-left: 40px;">客户和被适配者是解耦的，一个不知道另一个。</div><div><span style="font-size: 14pt; font-weight: bold;">二、图例</span></div><ol><li><div>类适配器：</div></li></ol><div><img src="6. 适配器（Adapter）模式_files/Image.png" type="image/png" data-filename="Image.png" width="573"/></div><ol start="2"><li><div>对象适配器（更通用）：</div></li></ol><div><img src="6. 适配器（Adapter）模式_files/Image [1].png" type="image/png" data-filename="Image.png" width="561"/></div><ul><li><div>Client：与符合Target接口的对象协同。调用Target提供的接口。</div></li><li><div>Target：定义Client使用的与特定领域相关的接口。要使用的接口，Adaptee的接口要向Target的接口靠拢。</div></li><li><div>Adapter：对Adaptee的接口与Target接口进行适配，适配器。</div><div>一般来说，适配器实现了目标接口，并持有被适配者的实例。</div></li><li><div>Adaptee：定义一个已经存在的接口，这个接口需要适配。需要适配接口的类，已经存在的类，其接口要向Target提供的接口靠拢。</div></li></ul><div>对象适配器 Vs. 类适配器</div><ul><li><div>对象适配器</div></li><ul><li><div>使用组合，不仅可以适配某个类，也可以适配该类的任何对象</div></li><li><div>只需要写一些代码，将工作委托给被适配者进行，这样更加有弹性</div></li></ul><li><div>类适配器</div></li><ul><li><div>不需要重新实现整个被适配者，必要的时候，可以覆盖被适配者的行为</div></li><li><div>需要多继承的语言支持</div></li></ul></ul><div><span style="font-size: 14pt; font-weight: bold;">三、优缺点</span></div><div><span style="font-weight: bold;">优点</span></div><ol><li><div>共同的优点：</div></li></ol><ul><li><div>将目标类与适配者类解耦，通过引入一个适配器类来重用现有的适配者类，无需修改原有的结构。</div></li><li><div>增加了类的透明性和复用性，将具体的业务实现过程封装在适配者类中，对于客户端而言是透明的，而且提高了适配者的复用性，同一个适配这类可以在多个不同的系统中复用。</div></li><li><div>灵活性和扩展性非常好，可以很方便的更换适配器，也可以在不修改原有代码的基础上增加新的适配器类，完全符合“开闭原则”。</div></li></ul><ol start="2"><li><div>类适配器的优点：</div></li></ol><ul><li><div>由于适配器类是适配者类的子类，因此可以在适配器类中置换一些适配者的方法（子类覆盖父类的方法），使得适配器的灵活性更强。</div></li></ul><ol start="3"><li><div>对象适配器的优点：</div></li></ol><ul><li><div>一个对象适配器（Adapter）可以把多个适配者（Adaptee）适配到同一个目标（Target）。</div></li><li><div>可以适配一个适配者的子类，由于适配器和适配者之间是关联关系，根据“里氏代换原则”，适配者的子类也可以通过该适配器进行适配。</div></li></ul><div><span style="font-weight: bold;">缺点</span></div><ol><li><div>类适配器的缺点：</div></li></ol><ul><li><div>对Java、C#等不支持多重类继承的语言，一次最多只能适配一个适配者类，不能同时适配多个适配者。</div></li><li><div>适配者类不能为最终类，如在Java中不能为final类，在C#中不能为sealed类。</div></li><li><div>在Java、C#等语言中，类适配器模式中的目标抽象类只能为接口，不能为类，其使用有一定的局限。</div></li></ul><ol start="2"><li><div>对象适配器的缺点：</div></li></ol><ul><li><div>在适配器中置换适配者的某些方法比较麻烦。（不能覆盖适配者类中提供的某些方法）</div></li></ul><div><span style="font-size: 14pt; font-weight: bold;">四、可能的改进</span></div><ol><li><div>双向适配器：Adaptee要使用Target中的方法，Target也要使用Adaptee中的方法。（很少使用）</div></li><li><div>缺省适配器（Default Adapter Pattern）：适用于不想使用一个接口中所有方法的情况，又称为单接口适配器模式。</div></li><li><div><span style="color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;;">Java语言中使用XML和反射机制确定使用哪个适配器。（</span><a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Java/src/com/company/adapter/sample01" style="font-family: &quot;Open Sans&quot;;">类适配器实现</a><span style="color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;;">，对象适配器实现）</span></div></li></ol><div><span style="font-size: 14pt; font-weight: bold;">五、个人理解</span></div><ul><li><div>适配器只负责接口之间的转换，一般与具体的实现无关。</div></li><li><div>适配器有类适配器和对象适配器的区别，一般对象适配器应用更广泛。（对象适配器对于覆盖Adaptee中的实现并不能很好的处理，但除此之外，均应使用对象适配器）</div></li><li><div>适配器模式比较简单，但是归根结底是一个补救措施，在设计和实现阶段不应使用，只有在无法拿到源码或修改代码代价巨大的情况下才考虑。</div></li></ul><div><font style="font-size: 14pt;"><span style="font-size: 14pt; font-weight: bold;">六、其他</span></font></div><div>      在C++的类适配器的实现中，Adapter要以public的方式继承Target，以private的方式继承Adaptee，这是因为：</div><ul><li><div>用户要使用Target，所以Adapter要体现“is-a” Target的关系。</div></li><li><div>Adapter和Adaptee是一种“is-implemented-in-terms-of（根据某物实现出）”的关系，根本上，使用的并不是Adaptee的接口，而是Adaptee的实现，所以以private继承时合理的。（当然也可以public的方式继承）</div></li></ul><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div></div><div><br/></div></span>
</div></body></html> 